package com.slink.fxfk.common.dao.imp;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.type.Type;

/**
 * HQL分页查询
 * @author 新
 *
 */
public class Finder {
	private StringBuilder hqlBuilder;

	private List<String> params;
	private List<Object> values;
	private List<Type> types;

	private List<String> paramsList;
	private List<Collection<Object>> valuesList;
	private List<Type> typesList;

	private List<String> paramsArray;
	private List<Object[]> valuesArray;
	private List<Type> typesArray;

	private int firstResult = 0;

	private int maxResults = 0;

	private boolean cacheable = false;

	public static final String ROW_COUNT = "select count(*) ";
	public static final String FROM = "from";
	public static final String DISTINCT = "distinct";
	public static final String HQL_FETCH = "fetch";
	public static final String ORDER_BY = "order";
	public static final String GROUP_BY = "group";

	protected Finder() {
		hqlBuilder = new StringBuilder();
	}

	protected Finder(String hql) {
		hqlBuilder = new StringBuilder(hql);
	}

	public Finder append(String hql) {
		hqlBuilder.append(hql);
		return this;
	}

	public static Finder create() {
		return new Finder();
	}

	public static Finder create(String hql) {
		return new Finder(hql);
	}

	/**
	 * 
	 * @方法描述：获得原始hql语句
	 * @所需参数： @return
	 * @返回类型： String
	 */
	public String getOrigHql() {
		return hqlBuilder.toString();
	}

	/**
	 * 
	 * @方法描述：获得查询数据库记录数的hql语句。
	 * @所需参数： @return
	 * @返回类型： String
	 */
	public String getRowCountHql() {
		return getRowCountBaseHql(ORDER_BY);
	}

	public String getRowCountTotalHql(String selectSql) {
		return getRowCountTotalBaseHql(ORDER_BY, selectSql);
	}

	public String getRowCountHqlByGroup() {
		return getRowCountBaseHql(GROUP_BY);
	}

	/**
	 * 
	 * @方法描述：获取起始位置
	 * @所需参数： @return
	 * @返回类型： int
	 */
	public int getFirstResult() {
		return firstResult;
	}

	public void setFirstResult(int firstResult) {
		this.firstResult = firstResult;
	}

	/**
	 * 
	 * @方法描述：获取每页条数
	 * @所需参数： @return
	 * @返回类型： int
	 */
	public int getMaxResults() {
		return maxResults;
	}

	/**
	 * 
	 * @参数 @param maxResults
	 * @返回类型 void
	 * @throws
	 * @说明：设置每页条数
	 */
	public void setMaxResults(int maxResults) {
		this.maxResults = maxResults;
	}

	/**
	 * 
	 * @方法描述：是否使用查询缓存
	 * @所需参数： @return
	 * @返回类型： boolean
	 */
	public boolean isCacheable() {
		return cacheable;
	}

	/**
	 * 
	 * @方法描述：设置是否使用查询缓存
	 * @所需参数： @param cacheable
	 * @返回类型： void
	 */
	public void setCacheable(boolean cacheable) {
		this.cacheable = cacheable;
	}

	/**
	 * 
	 * @方法描述：设置参数
	 * @所需参数： @param param
	 * @所需参数： @param value
	 * @所需参数： @return
	 * @返回类型： Finder
	 */
	public Finder setParam(String param, Object value) {
		return setParam(param, value, null);
	}


	/**
	 * 
	 * @方法描述：设置参数。与hibernate的Query接口一致。
	 * @所需参数： @param param
	 * @所需参数： @param value
	 * @所需参数： @param type
	 * @所需参数： @return
	 * @返回类型： Finder
	 */
	public Finder setParam(String param, Object value, Type type) {
		getParams().add(param);
		getValues().add(value);
		getTypes().add(type);
		return this;
	}


	/**
	 * 
	 * @方法描述：设置参数。与hibernate的Query接口一致。
	 * @所需参数： @param paramMap
	 * @所需参数： @return
	 * @返回类型： Finder
	 */
	public Finder setParams(Map<String, Object> paramMap) {
		for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
			setParam(entry.getKey(), entry.getValue());
		}
		return this;
	}

	/**
	 * 
	 * @方法描述：设置参数。与hibernate的Query接口一致。
	 * @所需参数： @param name
	 * @所需参数： @param vals
	 * @所需参数： @param type
	 * @所需参数： @return
	 */
	public Finder setParamList(String name, Collection<Object> vals, Type type) {
		getParamsList().add(name);
		getValuesList().add(vals);
		getTypesList().add(type);
		return this;
	}

	/**
	 * 
	 * @方法描述：设置参数。与hibernate的Query接口一致。
	 * @所需参数： @param name
	 * @所需参数： @param vals
	 * @所需参数： @return
	 * @返回类型： Finder
	 */
	public Finder setParamList(String name, Collection<Object> vals) {
		return setParamList(name, vals, null);
	}

	/**
	 * 
	 * @方法描述：设置参数。与hibernate的Query接口一致。
	 * @所需参数： @param name
	 * @所需参数： @param vals
	 * @所需参数： @param type
	 * @所需参数： @return
	 * @返回类型： Finder
	 */
	public Finder setParamList(String name, Object[] vals, Type type) {
		getParamsArray().add(name);
		getValuesArray().add(vals);
		getTypesArray().add(type);
		return this;
	}

	/**
	 * 
	 * @方法描述：设置参数。与hibernate的Query接口一致。
	 * @所需参数： @param name
	 * @所需参数： @param vals
	 * @所需参数： @return
	 * @返回类型： Finder
	 */
	public Finder setParamList(String name, Object[] vals) {
		return setParamList(name, vals, null);
	}

	/**
	 * 
	 * @方法描述：将finder中的参数设置到query中。
	 * @所需参数： @param query
	 * @所需参数： @return
	 * @返回类型： Query
	 */
	public Query setParamsToQuery(Query query) {
		if (params != null) {
			for (int i = 0; i < params.size(); i++) {
				if (types.get(i) == null) {
					query.setParameter(params.get(i), values.get(i));
				} else {
					query.setParameter(params.get(i), values.get(i),
							types.get(i));
				}
			}
		}
		if (paramsList != null) {
			for (int i = 0; i < paramsList.size(); i++) {
				if (typesList.get(i) == null) {
					query.setParameterList(paramsList.get(i), valuesList.get(i));
				} else {
					query.setParameterList(paramsList.get(i),
							valuesList.get(i), typesList.get(i));
				}
			}
		}
		if (paramsArray != null) {
			for (int i = 0; i < paramsArray.size(); i++) {
				if (typesArray.get(i) == null) {
					query.setParameterList(paramsArray.get(i),
							valuesArray.get(i));
				} else {
					query.setParameterList(paramsArray.get(i),
							valuesArray.get(i), typesArray.get(i));
				}
			}
		}
		return query;
	}

	/**
	 * 
	 * @方法描述：创建query对象
	 * @所需参数： @param s
	 * @所需参数： @return
	 * @返回类型： Query
	 */
	public Query createQuery(Session s) {
		Query query = setParamsToQuery(s.createQuery(getOrigHql()));
		if (getFirstResult() > 0) {
			query.setFirstResult(getFirstResult());
		}
		if (getMaxResults() > 0) {
			query.setMaxResults(getMaxResults());
		}
		if (isCacheable()) {
			query.setCacheable(true);
		}
		return query;
	}

	private String getRowCountBaseHql(String indexKey) {
		String hql = hqlBuilder.toString();

		int fromIndex = hql.toLowerCase().indexOf(FROM);
		String projectionHql = hql.substring(0, fromIndex);

		hql = hql.substring(fromIndex);
		String rowCountHql = hql.replace(HQL_FETCH, "");

		int index = rowCountHql.indexOf(indexKey);
		if (index > 0) {
			rowCountHql = rowCountHql.substring(0, index);
		}
		return wrapProjection(projectionHql) + rowCountHql;
	}

	private String getRowCountTotalBaseHql(String indexKey, String selectSql) {
		String hql = hqlBuilder.toString();

		int fromIndex = hql.toLowerCase().indexOf(FROM);
		String projectionHql = hql.substring(0, fromIndex);

		hql = hql.substring(fromIndex);
		String rowCountHql = hql.replace(HQL_FETCH, "");

		int index = rowCountHql.indexOf(indexKey);
		if (index > 0) {
			rowCountHql = rowCountHql.substring(0, index);
		}
		return "select count(bean) from " + "( " + projectionHql + rowCountHql
				+ ") as a";
	}

	private String wrapProjection(String projection) {
		if (projection.indexOf("select") == -1) {
			return ROW_COUNT;
		} else {
			return projection.replace("select", "select count(") + ") ";
		}
	}

	@SuppressWarnings("unused")
	private String wrapProjectionBeanId(String projection) {
		if (projection.indexOf("select") == -1) {
			return "select bean.id ";
		} else {
			return projection.replace("select bean", "select bean.id") + " ";
		}
	}

	/**
	 * 
	 * @方法描述：获取参数列表
	 * @所需参数： @return
	 * @返回类型： List<String>
	 */
	private List<String> getParams() {
		if (params == null) {
			params = new ArrayList<String>();
		}
		return params;
	}

	private List<Object> getValues() {
		if (values == null) {
			values = new ArrayList<Object>();
		}
		return values;
	}

	private List<Type> getTypes() {
		if (types == null) {
			types = new ArrayList<Type>();
		}
		return types;
	}

	private List<String> getParamsList() {
		if (paramsList == null) {
			paramsList = new ArrayList<String>();
		}
		return paramsList;
	}

	private List<Collection<Object>> getValuesList() {
		if (valuesList == null) {
			valuesList = new ArrayList<Collection<Object>>();
		}
		return valuesList;
	}

	private List<Type> getTypesList() {
		if (typesList == null) {
			typesList = new ArrayList<Type>();
		}
		return typesList;
	}

	private List<String> getParamsArray() {
		if (paramsArray == null) {
			paramsArray = new ArrayList<String>();
		}
		return paramsArray;
	}

	private List<Object[]> getValuesArray() {
		if (valuesArray == null) {
			valuesArray = new ArrayList<Object[]>();
		}
		return valuesArray;
	}

	private List<Type> getTypesArray() {
		if (typesArray == null) {
			typesArray = new ArrayList<Type>();
		}
		return typesArray;
	}
}
